package com.tbit.uqbike.service.business.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.common.entity.pojo.Dosage;
import com.tbit.common.utils.TbitHttpServiceUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.tbit.config.datasource.DataSource;
import com.tbit.uqbike.constant.*;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.pojo.AccountConfigExt;
import com.tbit.uqbike.object.pojo.BrandRemoteConfig;
import com.tbit.uqbike.object.pojo.MoveConfig;
import com.tbit.uqbike.service.base.RedisService;
import com.tbit.uqbike.service.business.AccountConfigExtService;
import com.tbit.uqbike.service.business.AccountService;
import com.tbit.uqbike.webmanager.business.machinegroup.object.vo.MachineGroupVo;
import com.tbit.uqbike.webmanager.business.machinegroup.service.MachineGroupService;
import com.tbit.uqbike.webmanager.business.userdivide.dao.core.UserDivideDao;
import com.tbit.uqbike.webmanager.dao.core.AccountConfigExtDao;
import com.tbit.uqbike.webmanager.dao.core.BrandRemoteConfigDao;
import com.tbit.uqbike.webmanager.dao.core.MachineDao;
import com.tbit.uqbike.webmanager.util.DateUtils;
import com.tbit.uqbike.webmanager.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 区域参数扩展配置Service业务层处理
 *
 * @author jing
 * @date 2021-04-21
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AccountConfigExtServiceImpl implements AccountConfigExtService {
    final AccountConfigExtDao accountConfigExtDao;
    final SensitivenessServiceImpl sensitivenessService;
    final BrandRemoteConfigDao configDao;
    final MachineGroupService machineGroupService;
    final UserDivideDao userDivideDao;
    final AccountService accountService;
    final MachineDao machineDao;

    @Resource
    private BrandRemoteConfigDao brandRemoteConfigDao;
    @Autowired
    private RedisService redisService;

    /**
     * 默认配置(可考虑配置到nacos)
     */
    private static final Map<String, String> defaultConfig;

    static {
        Map<String, String> aMap = new HashMap<>();
        aMap.put("rfidAbnormalDay", "15");
        aMap.put("spikeAbnormalDay", "15");
        aMap.put("face", "60");
        aMap.put("onlineCustomerService", "1");
        aMap.put("issueInvoiceSwitch", "1");
        aMap.put("temporaryPowerSwitch", "1");
        aMap.put("temporaryPowerTime", "1");
        aMap.put("faceTotalEveryDay", "20");
        aMap.put("faceTotalShortTime", "10");
        aMap.put("faceShortTime", "10");
        aMap.put("faceFailShortTime", "6");
        //是否开启拍照申诉还车 0:关闭 1:开启
        aMap.put("return_by_photo", "0");
        //是否开启拍二维码还车 0:关闭 1:开启
        aMap.put("return_by_qr", "0");
        //是否需要识别车牌号 0:不需要 1:需要
        aMap.put("identify_car_no", "0");
        defaultConfig = Collections.unmodifiableMap(aMap);
    }

    private boolean poll_count;


    /**
     * 查询区域参数扩展配置列表,当参数不存在时，添加默认值
     *
     * @param accountId 区域ID
     *                  paramKey 参数key
     * @return 区域参数扩展配置
     */
    @Override
    public List<AccountConfigExt> selectAndAddAccountConfigExtList(Integer accountId, String paramKey) {
        // 事实上前端查询不一定会传 paramKey，所以有可能查询出的是所有的配置项
        List<AccountConfigExt> configList = accountConfigExtDao.selectAccountConfigExtList(accountId, paramKey);
        Set<String> keySet = configList.stream()
                .map(AccountConfigExt::getParamKey)
                .collect(Collectors.toSet());

        checkAndFillDefaultValue("rfidAbnormalDay", "15", keySet, configList, accountId);
        checkAndFillDefaultValue("machineDivideSwitch", "0", keySet, configList, accountId);
        checkAndFillDefaultValue("onlineCustomerService", "1", keySet, configList, accountId);
        checkAndFillDefaultValue("face", "60", keySet, configList, accountId);
        checkAndFillDefaultValue("temporaryPowerSwitch", "1", keySet, configList, accountId);
        checkAndFillDefaultValue("temporaryPowerTime", "1", keySet, configList, accountId);
        checkAndFillDefaultValue("faceTotalEveryDay", "20", keySet, configList, accountId);
        checkAndFillDefaultValue("faceTotalShortTime", "10", keySet, configList, accountId);
        checkAndFillDefaultValue("faceShortTime", "10", keySet, configList, accountId);
        checkAndFillDefaultValue("faceFailShortTime", "6", keySet, configList, accountId);
        // 是否开启拍照申诉还车
        checkAndFillDefaultValue("return_by_photo", "0", keySet, configList, accountId);
        // 是否开启拍二维码还车
        checkAndFillDefaultValue("return_by_qr", "0", keySet, configList, accountId);
        // 是否需要识别车牌号
        checkAndFillDefaultValue("identify_car_no", "0", keySet, configList, accountId);
        checkAndFillDefaultValue("spikeAbnormalDay", "15", keySet, configList, accountId);
        checkAndFillDefaultValue("issueInvoiceSwitch", "1", keySet, configList, accountId);
        checkAndFillDefaultValue("isOpenAppraise", "0", keySet, configList, accountId);
        // 还车校验最大时长
        checkAndFillDefaultValue("POLL_COUNT", "6", keySet, configList, accountId);
        //	充值结束订单自动退余额
        checkAndFillDefaultValue("autoRefundBalance", "0", keySet, configList, accountId);
        // 电池锁预警开关
        checkAndFillDefaultValue("batteryLockSwitch", "1", keySet, configList, accountId);
        // 是否开启手机号身份信息认证（运营商三要素）
        checkAndFillDefaultValue("mobileNameAuthSwitch", "0", keySet, configList, accountId);
        //用户故障上报配置
        checkAndFillDefaultValue("userFaultUploadPhotoSwitch", "0", keySet, configList, accountId);
        /*还车允许偏移角度*/
        checkAndFillDefaultValue("angle", "20", keySet, configList, accountId);

        // 检查用户设置的认证年龄上下限是否满足设定
        // 年龄下限大于等于12（没有电动车16），小于等于设置的上限值
        // 年龄上限小于等于 100，大于等于设置的上限，没有就是 100
        // 判断区域内是否有运营自行车
        boolean isBikeExist = machineDao.checkIfExistBike(accountId) != null;

        String minAge = String.valueOf(isBikeExist ? ConfigConstant.CERTIFICATION_MIN_AGE_BIKE : ConfigConstant.CERTIFICATION_MIN_AGE_ELECTRIC_BIKE);
        String maxAge = String.valueOf(ConfigConstant.CERTIFICATION_MAX_AGE_DEFAULT);

        // 用户认证年龄上限和下限
        checkAndFillDefaultValue(ConfigConstant.CERTIFICATION_AGE_LIMIT_MAX_KEY, maxAge, keySet, configList, accountId);
        // 下限为 12~24的正整数，默认16，有自行车的品牌运营区域默认下限12
        checkAndFillDefaultValue(ConfigConstant.CERTIFICATION_AGE_LIMIT_MIN_KEY, minAge, keySet, configList, accountId);

        checkAndFillDefaultValue(ConfigConstant.NEARBY_BIKE_DISPLAY_RANGE_KEY, String.valueOf(ConfigConstant.DEFAULT_NEARBY_BIKE_DISPLAY_RANGE), keySet, configList, accountId);
        //新用户指南相关
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_SWITCH, "0", keySet, configList, accountId);
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_EFFECTPEOPLE, "NEW", keySet, configList, accountId);
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_EFFECTTIMES, "EVERYTIME_BORROW", keySet, configList, accountId);
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_COUNT, "0", keySet, configList, accountId);
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_PAGE, StudyGuideConstant.ALL_PAGE, keySet, configList, accountId);
        checkAndFillDefaultValue(StudyGuideConstant.NEW_USER_SKIPPAGE, "0", keySet, configList, accountId);
        //整体站点误差范围
        checkAndFillDefaultValue(ConfigConstant.ALL_PARK_POINT_ALLOW_RANGE, "20", keySet, configList, accountId);
        checkAndFillDefaultValue(ConfigConstant.DEFAULT_HELMET_TYPE, "0", keySet, configList, accountId);
        checkAndFillDefaultValue(ConfigConstant.OFF_PARK_POINT_ENABLE, "0", keySet, configList, accountId);
        checkAndFillDefaultValue(ConfigConstant.OFF_PARK_POINT_ALLOW_RANGE, "100", keySet, configList, accountId);
        checkAndFillDefaultValue(ConfigConstant.LONG_ORDER_REMINDER_SWITCH, "0", keySet, configList, accountId);
        checkAndFillDefaultValue(ConfigConstant.LONG_ORDER_REMINDER_TIME, "60", keySet, configList, accountId);

        return configList;
    }

    /**
     * 检查配置项是否存在，否则填充默认值
     */
    private void checkAndFillDefaultValue(String key, String defaultValue, Set<String> keySet, List<AccountConfigExt> configList, Integer accountId) {
        if (!keySet.contains(key)) {
            AccountConfigExt accountConfigExt1 = new AccountConfigExt();
            accountConfigExt1.setParamKey(key);
            accountConfigExt1.setParamValue(defaultValue);
            accountConfigExt1.setAccountId(accountId);
            accountConfigExt1.setUpdateTime(new Date());
            accountConfigExtDao.addAccountConfigExt(accountConfigExt1);
            configList.add(accountConfigExt1);
        }
    }

    @Override
    public String checkKeyUnique(AccountConfigExt accountConfigExt) {
        return StringUtils.isNotEmpty(accountConfigExtDao.selectAccountConfigExtList(accountConfigExt.getAccountId(), accountConfigExt.getParamKey())) ? UserConstants.NOT_UNIQUE : UserConstants.UNIQUE;
    }

    /**
     * 新增区域参数扩展配置
     *
     * @param
     * @return 结果
     */
    @Override
    @Transactional
    public int insertAccountConfigExt(List<AccountConfigExt> accountConfigExts) {
        List<AccountConfigExt> adds = new ArrayList<>();
        Integer accountId = accountConfigExts.get(0).getAccountId();
        List<String> paramKeys = accountConfigExtDao.getByAccountId(accountId);
        /**存在修改，不存在新增*/
        MoveConfig moveConfig = new MoveConfig();
        for (AccountConfigExt accountConfigExt : accountConfigExts) {
            //判断是否是运维端挪车配置
            if ("isMoveCarSameStation".equals(accountConfigExt.getParamKey())) {
                moveConfig.setAccountId(accountConfigExt.getAccountId());
                moveConfig.setSameParkPoint(Integer.parseInt(accountConfigExt.getParamValue()));
                moveConfig.setUpdateTime(DateUtils.getTime());
            } else if ("moveCarDistance".equals(accountConfigExt.getParamKey())) {
                moveConfig.setMoveDistance(Integer.parseInt(accountConfigExt.getParamValue()));
            }

            //是否打开 身份证+人脸识别实名认证
            if ("nameAuthType".equals(accountConfigExt.getParamKey()) && accountConfigExt.getParamValue().equals("2")){
                checkNameAuthType(accountConfigExt.getAccountId());
            }

            accountConfigExt.setUpdateTime(DateUtils.getNowDate());
            String face = "face";
            if (Objects.equals(face, accountConfigExt.getParamKey())) {
                int faceVal = Integer.parseInt(accountConfigExt.getParamValue());
                int min = 60;
                if (faceVal < min) {
                    throw new BaseException("人脸识别缓存时间默认60秒,且不能低于60秒");
                }
            }

            // 车辆分账开关校验
            if ("machineDivideSwitch".equals(accountConfigExt.getParamKey()) &&
                    "1".equals(accountConfigExt.getParamValue())) {
                validateMachineDivideSwitch(accountConfigExt);
            }

            if (paramKeys.contains(accountConfigExt.getParamKey())) {
                accountConfigExtDao.updateAccountConfigExt(accountConfigExt);
                redisService.del(Constants.ACCOUNTCONFIG_KEY_PREFIX + accountConfigExt.getAccountId());
            } else {
                adds.add(accountConfigExt);
            }

            /*超长订单提醒时间限制*/
            if (accountConfigExt.getParamKey().equals(ConfigConstant.LONG_ORDER_REMINDER_TIME) && StringUtils.isNotBlank(accountConfigExt.getParamValue())){
                int value = Integer.parseInt(accountConfigExt.getParamValue());
                if (value < 0 || value > 99999){
                    throw new BaseException("超长订单提醒时间限制为0~99999分钟");
                }
            }
        }

        Map<String, String> configMap = accountConfigExts.stream()
                .collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
        checkAge(configMap.get(ConfigConstant.CERTIFICATION_AGE_LIMIT_MIN_KEY), configMap.get(ConfigConstant.CERTIFICATION_AGE_LIMIT_MAX_KEY), accountId);

        if(configMap.get(ConfigConstant.NEARBY_BIKE_DISPLAY_RANGE_KEY)!=null){
            int range = Integer.parseInt(configMap.get(ConfigConstant.NEARBY_BIKE_DISPLAY_RANGE_KEY));
            if (range < ConfigConstant.NEARBY_MIN_RANGE || range > ConfigConstant.NEARBY_MAX_RANGE) {
                throw new BaseException("未满足显示范围限制：" + ConfigConstant.NEARBY_MIN_RANGE + "~" + ConfigConstant.NEARBY_MAX_RANGE);
            }
        }

        if (!adds.isEmpty()) {
            accountConfigExtDao.addBatch(adds);
        }
        return 1;
    }

    /**
     * 校验身份证+人脸识别实名认证方案
     * @param accountId 区域id
     */
    private void checkNameAuthType(Integer accountId){
        //获取区域归属品牌优驱API配置
        Integer brandId = accountService.getBrandIdByAccountId(accountId);
        BrandRemoteConfig brandRemoteConfig = brandRemoteConfigDao.getBrandRemoteConfig(brandId, BrandRemoteConfigConstant.TBIT_CONFIG);
        if (Objects.isNull(brandRemoteConfig)){
            throw new BaseException("请联系管理员配置优驱API，再启用【身份证+人脸识别】实名认证方案");
        }
    }

    /**
     * 实名认证年龄校验
     * 年龄下限大于等于12（没有电动车16），小于等于设置的上限值
     * 年龄上限小于等于 100，大于等于设置的上限，没有就是 65
     * 判断区域内是否有运营自行车
     */
    private void checkAge(String editMinAgeStr, String editMaxAgeStr, Integer accountId) {

        if (editMinAgeStr == null && editMaxAgeStr == null) {
            return;
        }

        Map<String, AccountConfigExt> configMap = accountConfigExtDao.selectAccountConfigExtList(accountId, null).stream()
                .collect(Collectors.toMap(AccountConfigExt::getParamKey, Function.identity()));

        boolean isBikeExist = machineDao.checkIfExistBike(accountId) != null;
        // 当做查询的时候如果没查到会插入默认值，由于查询一定是在修改前面的，所以其实这里数据库的配置值不可能为空
        // 检查用户设置的下限范围
        int minAgeLimit = isBikeExist ? ConfigConstant.CERTIFICATION_MIN_AGE_BIKE : ConfigConstant.CERTIFICATION_MIN_AGE_ELECTRIC_BIKE;
        int minAge = Optional.ofNullable(configMap.get(ConfigConstant.CERTIFICATION_AGE_LIMIT_MIN_KEY).getParamValue())
                .map(Integer::parseInt)
                // 如果有自行车运营，则设置默认最小年龄为 12，否则 16
                .orElse(minAgeLimit);

        int maxAge = Optional.ofNullable(configMap.get(ConfigConstant.CERTIFICATION_AGE_LIMIT_MAX_KEY).getParamValue())
                .map(Integer::parseInt)
                .orElse(ConfigConstant.CERTIFICATION_MAX_AGE_DEFAULT);

        if (editMinAgeStr != null) {
            int editMinAge = Integer.parseInt(editMinAgeStr);
            if (!(editMinAge <= maxAge && editMinAge >= minAgeLimit)) {
                throw new BaseException("未满足年龄范围限制：" + minAgeLimit + "~" + maxAge);
            }
        }
        if (editMaxAgeStr != null) {
            int editMaxAge = Integer.parseInt(editMaxAgeStr);
            if (!(editMaxAge >= minAge && editMaxAge <= ConfigConstant.CERTIFICATION_MAX_AGE)) {
                throw new BaseException("未满足年龄范围限制：" + minAge + "~" + ConfigConstant.CERTIFICATION_MAX_AGE);
            }
        }
    }

    private void validateMachineDivideSwitch(AccountConfigExt accountConfigExt) {
        int brandId = accountService.getBrandId(accountConfigExt.getAccountId());

        // 需开启车辆分账
        BrandRemoteConfig adapayConfig = configDao.getBrandRemoteConfig(brandId, BrandRemoteConfigConstant.ADAPAY_BUTTON);
        if (adapayConfig != null) {
            int button = Integer.parseInt(adapayConfig.getParamKV().substring(adapayConfig.getParamKV().indexOf("=") + 1));
            if (button != 1) {
                throw new BaseException("开启车辆分账时，需先打开分账开关");
            }
        } else {
            throw new BaseException("开启车辆分账时，需先打开分账开关");
        }

        // 需不开启全额分账才能开启车辆分账
        BrandRemoteConfig config = configDao.getBrandRemoteConfig(brandId, BrandRemoteConfigConstant.ADAPAY_MONEY_CASH_TYPE);
        if (config != null) {
            int button = Integer.parseInt(config.getParamKV().substring(config.getParamKV().indexOf("=") + 1));
            if (button == 1) {
                throw new BaseException("开启车辆分账时，不能开启全额分账");
            }
        }

        // 所有车辆分组都必须设置了分账比例
        List<MachineGroupVo> groups = machineGroupService.selectListWithCount(accountConfigExt.getAccountId(), null);
        Integer ratioGroupCount = userDivideDao.selectMachineGroupCount(accountConfigExt.getAccountId(), SubAccountConstant.ACTIVE);
        if (groups.size() != ratioGroupCount) {
            throw new BaseException("开启车辆分账时，所有车辆分组都必须设置分账比例");
        }
    }

    @Override
    public MoveConfig getMoveConfig(AccountConfigExt accountConfigExt) {
        //运维端挪车配置
        MoveConfig configExt = accountConfigExtDao.selectConfig(accountConfigExt.getAccountId());
        if (configExt == null) {
            configExt = new MoveConfig(accountConfigExt.getAccountId());
            accountConfigExtDao.insertMaintainMachineMoveConfig(configExt);
        }
        return configExt;
    }

    /**
     * 新增挪车配置
     *
     * @param moveConfig
     */
    @Override
    public void addMoveConfig(MoveConfig moveConfig) {
        moveConfig.setUpdateTime(DateUtils.getTime());
        accountConfigExtDao.updateMoveConfig(moveConfig);
    }

    @DS(DataSource.CORE)
    @Override
    public AccountConfigExt getByAccountIdAndType(Integer accountId, String key) {
        Map<String, Object> map = new HashMap<>();
        map.put("accountId", accountId);
        map.put("paramKey", key);
        return accountConfigExtDao.getByAccountIdAndType(map);
    }


    /**
     * 检验是否配置优驱API和运营商三要素用量
     * @param brandId   品牌id
     * @return  boolean
     */
    @Override
    public Map<String, Object> checkApiAndDosage(Integer brandId) {
        Map<String, Object> map = new HashMap<>();
        map.put("flag", false);
        map.put("remark", "");
        try {
            BrandRemoteConfig brandRemoteConfig = brandRemoteConfigDao.getBrandRemoteConfig(brandId, BrandRemoteConfigConstant.TBIT_CONFIG);
            if (Objects.isNull(brandRemoteConfig)) {
                map.put("remark", "请联系管理员配置优驱 API 的运营商三要素信息，配置完成后再启用该实名认证方案");
                return map;
            }

            JSONObject jsonObject = new JSONObject();
            String key = brandRemoteConfig.getParamKV();
            String[] strings = key.split("&");
            for (String s : strings) {
                jsonObject.put(s.substring(0, s.indexOf("=")), s.substring(s.indexOf("=") + 1));
            }
            JSONObject surplus = TbitHttpServiceUtil.getSurplus(jsonObject.getString("appId"), jsonObject.getString("nonce"), jsonObject.getString("appSecret"));
            if (Objects.nonNull(surplus)){
                boolean hasSufficientDosage = surplus.getJSONArray("data").stream()
                        .map(data -> JSON.parseObject(data.toString(), Dosage.class))
                        .anyMatch(dosage -> "运营商三要素".equals(dosage.getServiceName()) && dosage.getSurplusQuantity() > 0);

                if (hasSufficientDosage) {
                    map.put("flag", true);
                } else {
                    map.put("remark", "运营商三要素未开通或余量不足");
                }
            }
        }catch (Exception e){
            log.error("校验运营商三要素用量异常",e);
        }
        return map;
    }

}
